from pathlib import Path
from typing import Optional, Dict, List, cast


from rich.prompt import Prompt
from phi.api.workspace import log_workspace_event
from phi.api.schemas.workspace import (
    WorkspaceSchema,
    WorkspaceCreate,
    WorkspaceUpdate,
    WorkspaceEvent,
)
from phi.api.schemas.team import TeamSchema, TeamIdentifier
from phi.cli.config import PhiCliConfig
from phi.cli.console import (
    console,
    print_heading,
    print_info,
    print_subheading,
    log_config_not_available_msg,
)
from phi.infra.type import InfraType
from phi.infra.resources import InfraResources
from phi.workspace.config import WorkspaceConfig
from phi.workspace.enums import WorkspaceStarterTemplate
from phi.utils.common import str_to_int
from phi.utils.log import logger

TEMPLATE_TO_NAME_MAP: Dict[WorkspaceStarterTemplate, str] = {
    WorkspaceStarterTemplate.agent_app: "agent-app",
    WorkspaceStarterTemplate.agent_api: "agent-api",
}
TEMPLATE_TO_REPO_MAP: Dict[WorkspaceStarterTemplate, str] = {
    WorkspaceStarterTemplate.agent_app: "https://github.com/phidatahq/agent-app.git",
    WorkspaceStarterTemplate.agent_api: "https://github.com/phidatahq/agent-api.git",
}


def create_workspace(
    name: Optional[str] = None, template: Optional[str] = None, url: Optional[str] = None
) -> Optional[WorkspaceConfig]:
    """Creates a new workspace and returns the WorkspaceConfig.

    This function clones a template or url on the users machine at the path:
        cwd/name
    """
    import git
    from shutil import copytree

    from phi.cli.operator import initialize_phi
    from phi.utils.filesystem import rmdir_recursive
    from phi.workspace.helpers import get_workspace_dir_path
    from phi.utils.git import GitCloneProgress

    current_dir: Path = Path(".").resolve()

    # Phi should be initialized before creating a workspace
    phi_config: Optional[PhiCliConfig] = PhiCliConfig.from_saved_config()
    if not phi_config:
        phi_config = initialize_phi()
        if not phi_config:
            log_config_not_available_msg()
            return None
    phi_config = cast(PhiCliConfig, phi_config)

    ws_dir_name: Optional[str] = name
    repo_to_clone: Optional[str] = url
    ws_template = WorkspaceStarterTemplate.agent_app
    templates = list(WorkspaceStarterTemplate.__members__.values())

    if repo_to_clone is None:
        # Get repo_to_clone from template
        if template is None:
            # Get starter template from the user if template is not provided
            # Display available starter templates and ask user to select one
            print_info("Select starter template or press Enter for default (agent-app)")
            for template_id, template_name in enumerate(templates, start=1):
                print_info("  [b][{}][/b] {}".format(template_id, WorkspaceStarterTemplate(template_name).value))

            # Get starter template from the user
            template_choices = [str(idx) for idx, _ in enumerate(templates, start=1)]
            template_inp_raw = Prompt.ask("Template Number", choices=template_choices, default="1", show_choices=False)
            # Convert input to int
            template_inp = str_to_int(template_inp_raw)

            if template_inp is not None:
                template_inp_idx = template_inp - 1
                ws_template = WorkspaceStarterTemplate(templates[template_inp_idx])
        elif template.lower() in WorkspaceStarterTemplate.__members__.values():
            ws_template = WorkspaceStarterTemplate(template)
        else:
            raise Exception(f"{template} is not a supported template, please choose from: {templates}")

        logger.debug(f"Selected Template: {ws_template.value}")
        repo_to_clone = TEMPLATE_TO_REPO_MAP.get(ws_template)

    if ws_dir_name is None:
        default_ws_name = "agent-app"
        if url is not None:
            # Get default_ws_name from url
            default_ws_name = url.split("/")[-1].split(".")[0]
        else:
            # Get default_ws_name from template
            default_ws_name = TEMPLATE_TO_NAME_MAP.get(ws_template, "agent-app")
        logger.debug(f"Asking for ws name with default: {default_ws_name}")
        # Ask user for workspace name if not provided
        ws_dir_name = Prompt.ask("Workspace Name", default=default_ws_name, console=console)

    if ws_dir_name is None:
        logger.error("Workspace name is required")
        return None
    if repo_to_clone is None:
        logger.error("URL or Template is required")
        return None

    # Check if we can create the workspace in the current dir
    ws_root_path: Path = current_dir.joinpath(ws_dir_name)
    if ws_root_path.exists():
        logger.error(f"Directory {ws_root_path} exists, please delete directory or choose another name for workspace")
        return None

    print_info(f"Creating {str(ws_root_path)}")
    logger.debug("Cloning: {}".format(repo_to_clone))
    try:
        _cloned_git_repo: git.Repo = git.Repo.clone_from(
            repo_to_clone,
            str(ws_root_path),
            progress=GitCloneProgress(),  # type: ignore
        )
    except Exception as e:
        logger.error(e)
        return None

    # Remove existing .git folder
    _dot_git_folder = ws_root_path.joinpath(".git")
    _dot_git_exists = _dot_git_folder.exists()
    if _dot_git_exists:
        logger.debug(f"Deleting {_dot_git_folder}")
        try:
            _dot_git_exists = not rmdir_recursive(_dot_git_folder)
        except Exception as e:
            logger.warning(f"Failed to delete {_dot_git_folder}: {e}")
            logger.info("Please delete the .git folder manually")
            pass

    phi_config.add_new_ws_to_config(ws_root_path=ws_root_path)

    try:
        # workspace_dir_path is the path to the ws_root/workspace dir
        workspace_dir_path: Path = get_workspace_dir_path(ws_root_path)
        workspace_secrets_dir = workspace_dir_path.joinpath("secrets").resolve()
        workspace_example_secrets_dir = workspace_dir_path.joinpath("example_secrets").resolve()

        print_info(f"Creating {str(workspace_secrets_dir)}")
        copytree(
            str(workspace_example_secrets_dir),
            str(workspace_secrets_dir),
        )
    except Exception as e:
        logger.warning(f"Could not create workspace/secrets: {e}")
        logger.warning("Please manually copy workspace/example_secrets to workspace/secrets")

    print_info(f"Your new workspace is available at {str(ws_root_path)}\n")
    return setup_workspace(ws_root_path=ws_root_path)


def setup_workspace(ws_root_path: Path) -> Optional[WorkspaceConfig]:
    """Setup a phi workspace at `ws_root_path` and return the WorkspaceConfig

    1. Pre-requisites
    1.1 Check ws_root_path exists and is a directory
    1.2 Create PhiCliConfig if needed
    1.3 Create a WorkspaceConfig if needed
    1.4 Get the workspace name
    1.5 Get the git remote origin url
    1.6 Create anon user if needed

    2. Create or update WorkspaceSchema
    2.1 Check if a ws_schema exists for this workspace, meaning this workspace has a record in phi-api
    2.2 Create WorkspaceSchema if it doesn't exist
    2.3 Update WorkspaceSchema if git_url is updated
    """
    from rich.live import Live
    from rich.status import Status
    from phi.cli.operator import initialize_phi
    from phi.utils.git import get_remote_origin_for_dir
    from phi.workspace.helpers import get_workspace_dir_path

    print_heading("Setting up workspace\n")

    ######################################################
    ## 1. Pre-requisites
    ######################################################
    # 1.1 Check ws_root_path exists and is a directory
    ws_is_valid: bool = ws_root_path is not None and ws_root_path.exists() and ws_root_path.is_dir()
    if not ws_is_valid:
        logger.error("Invalid directory: {}".format(ws_root_path))
        return None

    # 1.2 Create PhiCliConfig if needed
    phi_config: Optional[PhiCliConfig] = PhiCliConfig.from_saved_config()
    if not phi_config:
        phi_config = initialize_phi()
        if not phi_config:
            log_config_not_available_msg()
            return None

    # 1.3 Create a WorkspaceConfig if needed
    logger.debug(f"Checking for a workspace at {ws_root_path}")
    ws_config: Optional[WorkspaceConfig] = phi_config.get_ws_config_by_path(ws_root_path)
    if ws_config is None:
        # There's no record of this workspace, reasons:
        # - The user is setting up a new workspace
        # - The user ran `phi init -r` which erased existing workspaces
        logger.debug(f"Could not find a workspace at: {ws_root_path}")

        # Check if the workspace contains a `workspace` dir
        workspace_ws_dir_path = get_workspace_dir_path(ws_root_path)
        logger.debug(f"Found the `workspace` configuration at: {workspace_ws_dir_path}")
        ws_config = phi_config.create_or_update_ws_config(ws_root_path=ws_root_path, set_as_active=True)
        if ws_config is None:
            logger.error(f"Failed to create WorkspaceConfig for {ws_root_path}")
            return None
    else:
        logger.debug(f"Found workspace at {ws_root_path}")

    # 1.4 Get the workspace name
    workspace_name = ws_root_path.stem.replace(" ", "-").replace("_", "-").lower()
    logger.debug(f"Workspace name: {workspace_name}")

    # 1.5 Get the git remote origin url
    git_remote_origin_url: Optional[str] = get_remote_origin_for_dir(ws_root_path)
    logger.debug("Git origin: {}".format(git_remote_origin_url))

    # 1.6 Create anon user if the user is not logged in
    if phi_config.user is None:
        from phi.api.user import create_anon_user

        logger.debug("Creating anon user")
        with Live(transient=True) as live_log:
            status = Status("Creating user...", spinner="aesthetic", speed=2.0, refresh_per_second=10)
            live_log.update(status)
            anon_user = create_anon_user()
            status.stop()
        if anon_user is not None:
            phi_config.user = anon_user

    ######################################################
    ## 2. Create or update WorkspaceSchema
    ######################################################
    # 2.1 Check if a ws_schema exists for this workspace, meaning this workspace has a record in phi-api
    ws_schema: Optional[WorkspaceSchema] = ws_config.ws_schema if ws_config is not None else None
    if phi_config.user is not None:
        # 2.2 Create WorkspaceSchema if it doesn't exist
        if ws_schema is None or ws_schema.id_workspace is None:
            from phi.api.team import get_teams_for_user
            from phi.api.workspace import create_workspace_for_user

            # If ws_schema is None, this is a NEW WORKSPACE.
            # We make a call to the api to create a new ws_schema
            logger.debug("Creating ws_schema")
            logger.debug(f"Getting teams for user: {phi_config.user.email}")
            teams: Optional[List[TeamSchema]] = None
            selected_team: Optional[TeamSchema] = None
            team_identifier: Optional[TeamIdentifier] = None
            with Live(transient=True) as live_log:
                status = Status(
                    "Checking for available teams...", spinner="aesthetic", speed=2.0, refresh_per_second=10
                )
                live_log.update(status)
                teams = get_teams_for_user(phi_config.user)
                status.stop()
            if teams is not None and len(teams) > 0:
                logger.debug(f"The user has {len(teams)} available teams. Checking if they want to use one of them")
                print_info("Which account would you like to create this workspace in?")
                print_info("  [b][1][/b] Personal (default)")
                for team_idx, team_schema in enumerate(teams, start=2):
                    print_info("  [b][{}][/b] {}".format(team_idx, team_schema.name))

                account_choices = ["1"] + [str(idx) for idx, _ in enumerate(teams, start=2)]
                account_inp_raw = Prompt.ask("Account Number", choices=account_choices, default="1", show_choices=False)
                account_inp = str_to_int(account_inp_raw)

                if account_inp is not None:
                    if account_inp == 1:
                        print_info("Creating workspace in your personal account")
                    else:
                        selected_team = teams[account_inp - 2]
                        print_info(f"Creating workspace in {selected_team.name}")
                        team_identifier = TeamIdentifier(id_team=selected_team.id_team, team_url=selected_team.url)

            with Live(transient=True) as live_log:
                status = Status("Creating workspace...", spinner="aesthetic", speed=2.0, refresh_per_second=10)
                live_log.update(status)
                ws_schema = create_workspace_for_user(
                    user=phi_config.user,
                    workspace=WorkspaceCreate(
                        ws_name=workspace_name,
                        git_url=git_remote_origin_url,
                    ),
                    team=team_identifier,
                )
                status.stop()

            logger.debug(f"Workspace created: {workspace_name}")
            if selected_team is not None:
                logger.debug(f"Selected team: {selected_team.name}")
            ws_config = phi_config.create_or_update_ws_config(
                ws_root_path=ws_root_path, ws_schema=ws_schema, ws_team=selected_team, set_as_active=True
            )

        # 2.3 Update WorkspaceSchema if git_url is updated
        if git_remote_origin_url is not None and ws_schema is not None and ws_schema.git_url != git_remote_origin_url:
            from phi.api.workspace import update_workspace_for_user, update_workspace_for_team

            logger.debug("Updating workspace")
            logger.debug(f"Existing git_url: {ws_schema.git_url}")
            logger.debug(f"New git_url: {git_remote_origin_url}")

            if ws_config is not None and ws_config.ws_team is not None:
                updated_workspace_schema = update_workspace_for_team(
                    user=phi_config.user,
                    workspace=WorkspaceUpdate(
                        id_workspace=ws_schema.id_workspace,
                        git_url=git_remote_origin_url,
                    ),
                    team=TeamIdentifier(id_team=ws_config.ws_team.id_team, team_url=ws_config.ws_team.url),
                )
            else:
                updated_workspace_schema = update_workspace_for_user(
                    user=phi_config.user,
                    workspace=WorkspaceUpdate(
                        id_workspace=ws_schema.id_workspace,
                        git_url=git_remote_origin_url,
                    ),
                )
            if updated_workspace_schema is not None:
                # Update the ws_schema for this workspace.
                ws_config = phi_config.create_or_update_ws_config(
                    ws_root_path=ws_root_path, ws_schema=updated_workspace_schema, set_as_active=True
                )
            else:
                logger.debug("Failed to update workspace. Please setup again")

    if ws_config is not None:
        # logger.debug("Workspace Config: {}".format(ws_config.model_dump_json(indent=2)))
        print_subheading("Setup complete! Next steps:")
        print_info("1. Start workspace:")
        print_info("\tphi ws up")
        print_info("2. Stop workspace:")
        print_info("\tphi ws down")
        if ws_config.workspace_settings is not None:
            scripts_dir = ws_config.workspace_settings.scripts_dir
            install_ws_file = f"sh {ws_root_path}/{scripts_dir}/install.sh"
            print_info("3. Install workspace dependencies:")
            print_info(f"\t{install_ws_file}")

        if ws_config.ws_schema is not None and phi_config.user is not None:
            log_workspace_event(
                user=phi_config.user,
                workspace_event=WorkspaceEvent(
                    id_workspace=ws_config.ws_schema.id_workspace,
                    event_type="setup",
                    event_status="success",
                    event_data={"workspace_root_path": str(ws_root_path)},
                ),
            )
        return ws_config
    else:
        print_info("Workspace setup unsuccessful. Please try again.")
    return None
    ######################################################
    ## End Workspace setup
    ######################################################


def start_workspace(
    phi_config: PhiCliConfig,
    ws_config: WorkspaceConfig,
    target_env: Optional[str] = None,
    target_infra: Optional[InfraType] = None,
    target_group: Optional[str] = None,
    target_name: Optional[str] = None,
    target_type: Optional[str] = None,
    dry_run: Optional[bool] = False,
    auto_confirm: Optional[bool] = False,
    force: Optional[bool] = None,
    pull: Optional[bool] = False,
) -> None:
    """Start a Phi Workspace. This is called from `phi ws up`"""
    if ws_config is None:
        logger.error("WorkspaceConfig invalid")
        return

    # Set the local environment variables before processing configs
    ws_config.set_local_env()

    # Get resource groups to deploy
    resource_groups_to_create: List[InfraResources] = ws_config.get_resources(
        env=target_env,
        infra=target_infra,
        order="create",
    )

    # Track number of resource groups created
    num_rgs_created = 0
    num_rgs_to_create = len(resource_groups_to_create)
    # Track number of resources created
    num_resources_created = 0
    num_resources_to_create = 0

    if num_rgs_to_create == 0:
        print_info("No resources to create")
        return

    logger.debug(f"Deploying {num_rgs_to_create} resource groups")
    for rg in resource_groups_to_create:
        _num_resources_created, _num_resources_to_create = rg.create_resources(
            group_filter=target_group,
            name_filter=target_name,
            type_filter=target_type,
            dry_run=dry_run,
            auto_confirm=auto_confirm,
            force=force,
            pull=pull,
        )
        if _num_resources_created > 0:
            num_rgs_created += 1
        num_resources_created += _num_resources_created
        num_resources_to_create += _num_resources_to_create
        logger.debug(f"Deployed {num_resources_created} resources in {num_rgs_created} resource groups")

    if dry_run:
        return

    if num_resources_created == 0:
        return

    print_heading(f"\n--**-- ResourceGroups deployed: {num_rgs_created}/{num_rgs_to_create}\n")

    workspace_event_status = "in_progress"
    if num_resources_created == num_resources_to_create:
        workspace_event_status = "success"
    else:
        logger.error("Some resources failed to create, please check logs")
        workspace_event_status = "failed"

    if phi_config.user is not None and ws_config.ws_schema is not None and ws_config.ws_schema.id_workspace is not None:
        # Log workspace start event
        log_workspace_event(
            user=phi_config.user,
            workspace_event=WorkspaceEvent(
                id_workspace=ws_config.ws_schema.id_workspace,
                event_type="start",
                event_status=workspace_event_status,
                event_data={
                    "target_env": target_env,
                    "target_infra": target_infra,
                    "target_group": target_group,
                    "target_name": target_name,
                    "target_type": target_type,
                    "dry_run": dry_run,
                    "auto_confirm": auto_confirm,
                    "force": force,
                },
            ),
        )


def stop_workspace(
    phi_config: PhiCliConfig,
    ws_config: WorkspaceConfig,
    target_env: Optional[str] = None,
    target_infra: Optional[InfraType] = None,
    target_group: Optional[str] = None,
    target_name: Optional[str] = None,
    target_type: Optional[str] = None,
    dry_run: Optional[bool] = False,
    auto_confirm: Optional[bool] = False,
    force: Optional[bool] = None,
) -> None:
    """Stop a Phi Workspace. This is called from `phi ws down`"""
    if ws_config is None:
        logger.error("WorkspaceConfig invalid")
        return

    # Set the local environment variables before processing configs
    ws_config.set_local_env()

    # Get resource groups to delete
    resource_groups_to_delete: List[InfraResources] = ws_config.get_resources(
        env=target_env,
        infra=target_infra,
        order="delete",
    )

    # Track number of resource groups deleted
    num_rgs_deleted = 0
    num_rgs_to_delete = len(resource_groups_to_delete)
    # Track number of resources deleted
    num_resources_deleted = 0
    num_resources_to_delete = 0

    if num_rgs_to_delete == 0:
        print_info("No resources to delete")
        return

    logger.debug(f"Deleting {num_rgs_to_delete} resource groups")
    for rg in resource_groups_to_delete:
        _num_resources_deleted, _num_resources_to_delete = rg.delete_resources(
            group_filter=target_group,
            name_filter=target_name,
            type_filter=target_type,
            dry_run=dry_run,
            auto_confirm=auto_confirm,
            force=force,
        )
        if _num_resources_deleted > 0:
            num_rgs_deleted += 1
        num_resources_deleted += _num_resources_deleted
        num_resources_to_delete += _num_resources_to_delete
        logger.debug(f"Deleted {num_resources_deleted} resources in {num_rgs_deleted} resource groups")

    if dry_run:
        return

    if num_resources_deleted == 0:
        return

    print_heading(f"\n--**-- ResourceGroups deleted: {num_rgs_deleted}/{num_rgs_to_delete}\n")

    workspace_event_status = "in_progress"
    if num_resources_to_delete == num_resources_deleted:
        workspace_event_status = "success"
    else:
        logger.error("Some resources failed to delete, please check logs")
        workspace_event_status = "failed"

    if phi_config.user is not None and ws_config.ws_schema is not None and ws_config.ws_schema.id_workspace is not None:
        # Log workspace stop event
        log_workspace_event(
            user=phi_config.user,
            workspace_event=WorkspaceEvent(
                id_workspace=ws_config.ws_schema.id_workspace,
                event_type="stop",
                event_status=workspace_event_status,
                event_data={
                    "target_env": target_env,
                    "target_infra": target_infra,
                    "target_group": target_group,
                    "target_name": target_name,
                    "target_type": target_type,
                    "dry_run": dry_run,
                    "auto_confirm": auto_confirm,
                    "force": force,
                },
            ),
        )


def update_workspace(
    phi_config: PhiCliConfig,
    ws_config: WorkspaceConfig,
    target_env: Optional[str] = None,
    target_infra: Optional[InfraType] = None,
    target_group: Optional[str] = None,
    target_name: Optional[str] = None,
    target_type: Optional[str] = None,
    dry_run: Optional[bool] = False,
    auto_confirm: Optional[bool] = False,
    force: Optional[bool] = None,
    pull: Optional[bool] = False,
) -> None:
    """Update a Phi Workspace. This is called from `phi ws patch`"""
    if ws_config is None:
        logger.error("WorkspaceConfig invalid")
        return

    # Set the local environment variables before processing configs
    ws_config.set_local_env()

    # Get resource groups to update
    resource_groups_to_update: List[InfraResources] = ws_config.get_resources(
        env=target_env,
        infra=target_infra,
        order="create",
    )
    # Track number of resource groups updated
    num_rgs_updated = 0
    num_rgs_to_update = len(resource_groups_to_update)
    # Track number of resources updated
    num_resources_updated = 0
    num_resources_to_update = 0

    if num_rgs_to_update == 0:
        print_info("No resources to update")
        return

    logger.debug(f"Updating {num_rgs_to_update} resource groups")
    for rg in resource_groups_to_update:
        _num_resources_updated, _num_resources_to_update = rg.update_resources(
            group_filter=target_group,
            name_filter=target_name,
            type_filter=target_type,
            dry_run=dry_run,
            auto_confirm=auto_confirm,
            force=force,
            pull=pull,
        )
        if _num_resources_updated > 0:
            num_rgs_updated += 1
        num_resources_updated += _num_resources_updated
        num_resources_to_update += _num_resources_to_update
        logger.debug(f"Updated {num_resources_updated} resources in {num_rgs_updated} resource groups")

    if dry_run:
        return

    if num_resources_updated == 0:
        return

    print_heading(f"\n--**-- ResourceGroups updated: {num_rgs_updated}/{num_rgs_to_update}\n")

    workspace_event_status = "in_progress"
    if num_resources_updated == num_resources_to_update:
        workspace_event_status = "success"
    else:
        logger.error("Some resources failed to update, please check logs")
        workspace_event_status = "failed"

    if phi_config.user is not None and ws_config.ws_schema is not None and ws_config.ws_schema.id_workspace is not None:
        # Log workspace start event
        log_workspace_event(
            user=phi_config.user,
            workspace_event=WorkspaceEvent(
                id_workspace=ws_config.ws_schema.id_workspace,
                event_type="update",
                event_status=workspace_event_status,
                event_data={
                    "target_env": target_env,
                    "target_infra": target_infra,
                    "target_group": target_group,
                    "target_name": target_name,
                    "target_type": target_type,
                    "dry_run": dry_run,
                    "auto_confirm": auto_confirm,
                    "force": force,
                },
            ),
        )


def delete_workspace(phi_config: PhiCliConfig, ws_to_delete: Optional[List[Path]]) -> None:
    if ws_to_delete is None or len(ws_to_delete) == 0:
        print_heading("No workspaces to delete")
        return

    for ws_root in ws_to_delete:
        phi_config.delete_ws(ws_root_path=ws_root)


def set_workspace_as_active(ws_dir_name: Optional[str]) -> None:
    from phi.cli.operator import initialize_phi

    ######################################################
    ## 1. Validate Pre-requisites
    ######################################################
    ######################################################
    # 1.1 Check PhiConf is valid
    ######################################################
    phi_config: Optional[PhiCliConfig] = PhiCliConfig.from_saved_config()
    if not phi_config:
        phi_config = initialize_phi()
        if not phi_config:
            log_config_not_available_msg()
            return

    ######################################################
    # 1.2 Check ws_root_path is valid
    ######################################################
    # By default, we assume this command is run from the workspace directory
    ws_root_path: Optional[Path] = None
    if ws_dir_name is None:
        # If the user does not provide a ws_name, that implies `phi set` is ran from
        # the workspace directory.
        ws_root_path = Path(".").resolve()
    else:
        # If the user provides a workspace name manually, we find the dir for that ws
        ws_config: Optional[WorkspaceConfig] = phi_config.get_ws_config_by_dir_name(ws_dir_name)
        if ws_config is None:
            logger.error(f"Could not find workspace {ws_dir_name}")
            return
        ws_root_path = ws_config.ws_root_path

    ws_dir_is_valid: bool = ws_root_path is not None and ws_root_path.exists() and ws_root_path.is_dir()
    if not ws_dir_is_valid:
        logger.error("Invalid workspace directory: {}".format(ws_root_path))
        return

    ######################################################
    # 1.3 Validate WorkspaceConfig is available i.e. a workspace is available at this directory
    ######################################################
    logger.debug(f"Checking for a workspace at path: {ws_root_path}")
    active_ws_config: Optional[WorkspaceConfig] = phi_config.get_ws_config_by_path(ws_root_path)
    if active_ws_config is None:
        # This happens when the workspace is not yet setup
        print_info(f"Could not find a workspace at path: {ws_root_path}")
        # TODO: setup automatically for the user
        print_info("If this workspace has not been setup, please run `phi ws setup` from the workspace directory")
        return

    ######################################################
    ## 2. Set workspace as active
    ######################################################
    print_heading(f"Setting workspace {active_ws_config.ws_root_path.stem} as active")
    phi_config.set_active_ws_dir(active_ws_config.ws_root_path)
    print_info("Active workspace updated")
    return
